﻿using System;
using System.Collections.Generic;
using System.Linq;
using BroodWar;
using BroodWar.Addon;
using BroodWar.Api;
using BroodWar.Api.Enum;
using BroodWar.Terrain;
using UnitType = BroodWar.Api.Enum.UnitType;

namespace BasicAIModule
{
	public class BasicAiModule : AiBase
	{
		List<AiBase> _modules;
		IBuildingPlacer _buildingPlacer;
		EnhancedUi _enhancedUi;
		bool _drawTasks;
		bool _drawAssignments;
		bool _drawResources;
		bool _drawLarva;

		public override void OnStart()
		{
			Game.EnableFlag(Flag.UserInput);
			TerrainAnalyzer.ReadMap();
			TerrainAnalyzer.Analyze();

			BorderManager.Create(InformationManager.Instance);
			BaseManager.Create(BorderManager.Instance);
			_buildingPlacer = new BFSBuildingPlacer();
			BuildEventTimeline.Create(BuildUnitManager.Instance);
			TaskScheduler.Create(BuildEventTimeline.Instance, BuildUnitManager.Instance);
			TaskExecutor.Create(UnitArbitrator.Instance, BuildEventTimeline.Instance, ReservedMap.Instance, _buildingPlacer);
			WorkerManager.Create(UnitArbitrator.Instance, BaseManager.Instance);
			ScoutManager.Create(UnitArbitrator.Instance, InformationManager.Instance);
			DefenseManager.Create(UnitArbitrator.Instance, BorderManager.Instance);
			BuildOrderManager.Create(TaskScheduler.Instance, TaskExecutor.Instance, BuildUnitManager.Instance);
			SupplyManager.Create(BuildOrderManager.Instance, TaskScheduler.Instance);
			_enhancedUi = new EnhancedUi();

			BuildEventTimeline.Instance.Initialize();
			ScoutManager.Instance.Initialize();

			_modules = new List<AiBase>
			{
				InformationManager.Instance,
				BorderManager.Instance,
				BaseManager.Instance,
				BuildEventTimeline.Instance,
				ReservedMap.Instance,
				UnitGroupManager.Instance,
				BuildUnitManager.Instance,
				TaskScheduler.Instance,
				TaskExecutor.Instance,
				WorkerManager.Instance,
				ScoutManager.Instance,
				DefenseManager.Instance,
			};

			var race = Game.Self.Race;

			var buildID = 1;
			if (race.TypeEquals(RaceType.Zerg))
			{
				// Send an overlord out if Zerg
				ScoutManager.Instance.SetScoutCount(1);

				if (buildID == 1)
				{
					//morph 5 lurkers (tests dependency resolver, task scheduler)
					BuildOrderManager.Instance.Build(8, UnitType.Zerg_Drone, 90);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Zerg_Overlord, 85);
					BuildOrderManager.Instance.Build(12, UnitType.Zerg_Drone, 84);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Zerg_Lair, 82);
					BuildOrderManager.Instance.BuildAdditional(5, UnitType.Zerg_Lurker, 80);
					BuildOrderManager.Instance.Build(12, UnitType.Zerg_Drone, 30);
				}
				else if (buildID >= 2)
				{
					//12 pool 6 lings (tests larva task scheduler)
					BuildOrderManager.Instance.Build(8, UnitType.Zerg_Drone, 90);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Zerg_Overlord, 85);
					BuildOrderManager.Instance.Build(12, UnitType.Zerg_Drone, 84);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Zerg_Spawning_Pool, 70);
					BuildOrderManager.Instance.BuildAdditional(3, UnitType.Zerg_Zergling, 65);
					BuildOrderManager.Instance.Build(40, UnitType.Zerg_Drone, 62);
				}
			}
			else if (race.TypeEquals(RaceType.Terran))
			{
				if (buildID == 1)
				{
					//build 3 comsat stations (tests dependency resolver)

					BuildOrderManager.Instance.Build(9, UnitType.Terran_SCV, 90);
					BuildOrderManager.Instance.Build(1, UnitType.Terran_Supply_Depot, 85);
					BuildOrderManager.Instance.Build(20, UnitType.Terran_SCV, 80);
					BuildOrderManager.Instance.BuildAdditional(3, UnitType.Terran_Comsat_Station, 60);
				}
				else if (buildID == 2)
				{
					//a complex build order
					BuildOrderManager.Instance.Build(9, UnitType.Terran_SCV, 90);
					BuildOrderManager.Instance.Build(1, UnitType.Terran_Supply_Depot, 85);
					BuildOrderManager.Instance.Build(20, UnitType.Terran_SCV, 80);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Terran_Barracks, 60);
					BuildOrderManager.Instance.BuildAdditional(9, UnitType.Terran_Marine, 45);

					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Terran_Refinery, 42);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Terran_Barracks, 40);
					BuildOrderManager.Instance.BuildAdditional(1, UnitType.Terran_Academy, 39);
					BuildOrderManager.Instance.BuildAdditional(9, UnitType.Terran_Medic, 38);


					BuildOrderManager.Instance.Research(TechType.Stim_Packs, 35);
					BuildOrderManager.Instance.Research(TechType.Tank_Siege_Mode, 35);
					BuildOrderManager.Instance.BuildAdditional(3, UnitType.Terran_Siege_Tank_Tank_Mode, 34);
					BuildOrderManager.Instance.BuildAdditional(2, UnitType.Terran_Science_Vessel, 30);
					BuildOrderManager.Instance.Research(TechType.Irradiate, 30);

					BuildOrderManager.Instance.Upgrade(1, UpgradeType.Terran_Infantry_Weapons, 20);
					BuildOrderManager.Instance.Build(3, UnitType.Terran_Missile_Turret, 13);
					BuildOrderManager.Instance.Upgrade(3, UpgradeType.Terran_Infantry_Weapons, 12);
					BuildOrderManager.Instance.Upgrade(3, UpgradeType.Terran_Infantry_Armor, 12);
					BuildOrderManager.Instance.Build(1, UnitType.Terran_Engineering_Bay, 11);
					BuildOrderManager.Instance.BuildAdditional(40, UnitType.Terran_Marine, 10);
					BuildOrderManager.Instance.Build(6, UnitType.Terran_Barracks, 8);
					BuildOrderManager.Instance.Build(2, UnitType.Terran_Engineering_Bay, 7);
					BuildOrderManager.Instance.BuildAdditional(10, UnitType.Terran_Siege_Tank_Tank_Mode, 5);
				}
				else if (buildID >= 3)
				{
					//terran mech - tests task ordering with add-on requiring units
					BuildOrderManager.Instance.Build(9, UnitType.Terran_SCV, 90);
					BuildOrderManager.Instance.Build(1, UnitType.Terran_Supply_Depot, 85);
					BuildOrderManager.Instance.Build(20, UnitType.Terran_SCV, 80);
					BuildOrderManager.Instance.BuildAdditional(2, UnitType.Terran_Machine_Shop, 70);
					BuildOrderManager.Instance.BuildAdditional(3, UnitType.Terran_Factory, 60);
					BuildOrderManager.Instance.Research(TechType.Spider_Mines, 55);
					BuildOrderManager.Instance.Research(TechType.Tank_Siege_Mode, 55);
					BuildOrderManager.Instance.BuildAdditional(20, UnitType.Terran_Vulture, 40);
					BuildOrderManager.Instance.BuildAdditional(20, UnitType.Terran_Siege_Tank_Tank_Mode, 40);
					BuildOrderManager.Instance.Upgrade(3, UpgradeType.Terran_Vehicle_Weapons, 20);
				}

			}
			else if (race.TypeEquals(RaceType.Protoss))
			{
				//build 20 carriers - tests dependency resolver
				BuildOrderManager.Instance.Build(8, UnitType.Protoss_Probe, 90);
				BuildOrderManager.Instance.Build(1, UnitType.Protoss_Pylon, 85);
				BuildOrderManager.Instance.Build(20, UnitType.Protoss_Probe, 80);
				BuildOrderManager.Instance.BuildAdditional(10, UnitType.Protoss_Dragoon, 70);
				BuildOrderManager.Instance.BuildAdditional(10, UnitType.Protoss_Zealot, 70);
				BuildOrderManager.Instance.Upgrade(1, UpgradeType.Singularity_Charge, 61);
				BuildOrderManager.Instance.BuildAdditional(20, UnitType.Protoss_Carrier, 60);
			}
			_drawTasks = true;
			_drawAssignments = false;
			_drawResources = true;
			_drawLarva = race.TypeEquals(RaceType.Zerg);
		}

		public override void OnFrame()
		{
			if (Game.IsPaused)
				return;

			UnitArbitrator.Instance.Update();
			BuildEventTimeline.Instance.Reset();

			foreach (var module in _modules)
			{
				module.OnFrame();
			}

			_enhancedUi.Update();

			BorderManager.Instance.Draw();

			if (!InformationManager.Instance.EnemyBases.Any())
			{
				if (Game.FrameCount > 2*24*60)
				{
					ScoutManager.Instance.SetScoutCount(1);
				}
			}
			else
			{
				ScoutManager.Instance.SetScoutCount(0);
			}

			BuildEventTimeline.Instance.InitialState.CreateUnclaimedBuildUnits();

			BuildOrderManager.Instance.OnFrame();
			SupplyManager.Instance.OnFrame();

			if (_drawResources)
			{
				BuildEventTimeline.Instance.Draw();
			}
			if (_drawLarva)
			{
				BuildEventTimeline.Instance.DrawLarvaCounts();
			}
			Game.DrawTextScreen(
				0,
				0,
				"Time: {0}, Minerals: {1}, Gas: {2}",
				BuildEventTimeline.Instance.InitialState.Time,
				BuildEventTimeline.Instance.InitialState.Minerals,
				BuildEventTimeline.Instance.InitialState.Gas);
			const int y = 50 - 16;
			if (_drawTasks)
			{
				BuildOrderManager.Instance.Draw(20, y);
			}

			#warning Not yet implemented
			/*if(!_drawAssignments)
				return;

			foreach (var unit in Game.Self.Units)
			{
				if(!UnitArbitrator.Instance.HasBid(unit))
					continue;
				int x = unit.Position.X;
				int y = unit.Position.Y;
				std::list< std::pair< UnitController*, double > > bids = m_unitArbitrator->getAllBidders( i );
				int y_off = 0;
				bool first = false;
				const char activeColor = '\x07', inactiveColor = '\x16';
				char color = activeColor;
				for ( std::list< std::pair< UnitController*, double > >::iterator j = bids.begin(); j != bids.end(); j++ )
				{
					Broodwar->drawTextMap( x, y + y_off, "%c%s: %d", color, j->first->getName().c_str(), (int)j->second );
					y_off += 15;
					color = inactiveColor;
				}
			}*/
		}

		public override void OnSendText(string text)
		{
			foreach( var module in _modules )
				module.OnSendText(text);

			switch (text)
			{
				case "t":
					_drawTasks = !_drawTasks;
					break;
				case "a":
					_drawAssignments = !_drawAssignments;
					break;
				case "r":
					_drawResources = !_drawResources;
					break;
				case "l":
					_drawLarva = !_drawLarva;
					break;
				case "expand":
					BaseManager.Instance.ExpandNow();
					break;
				default:
					BuildType type;
					if (Enum.TryParse(text, true, out type))
					{
						BuildOrderManager.Instance.BuildAdditional(1, type, 300);
					}
					else
					{
						Game.Write("Unrecognized command: '{0}'", text);
						Game.SendText(text);
					}
					break;
			}
		}

		public override void OnUnitDiscover(Unit unit)
		{
			foreach (var module in _modules)
			{
				module.OnUnitDiscover(unit);
			}
		}

		public override void OnUnitEvade(Unit unit)
		{
			foreach (var module in _modules)
			{
				module.OnUnitEvade(unit);
			}
		}

		public override void OnUnitDestroy(Unit unit)
		{
			UnitArbitrator.Instance.OnRemoveObject(unit);
			foreach (var module in _modules)
			{
				module.OnUnitDestroy(unit);
			}
		}

		public override void OnUnitMorph(Unit unit)
		{
			foreach (var module in _modules)
			{
				module.OnUnitMorph(unit);
			}
		}

		public override void OnUnitRenegade(Unit unit)
		{
			foreach (var module in _modules)
			{
				module.OnUnitRenegade(unit);
			}
		}

		public override void OnUnitComplete(Unit unit)
		{
			foreach (var module in _modules)
			{
				module.OnUnitComplete(unit);
			}
		}
	}
}
